package org.agony2d.gpu.core {
	import flash.display.Stage;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DTriangleFace;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.getTimer;
	
	import org.agony2d.Agony;
	import org.agony2d.core.IFrameUpdater;
	import org.agony2d.core.UpdaterManager;
	import org.agony2d.core.agony_internal;
	import org.agony2d.debug.Logger;
	import org.agony2d.gpu.supportClasses.buffers.BuffersManager;
	import org.agony2d.gpu.supportClasses.programs.ProgramProxy;
	import org.agony2d.gpu.supportClasses.textures.TextureProxy;
	import org.agony2d.input.ATouchEvent;
	import org.agony2d.input.Touch;
	import org.agony2d.input.TouchManager;
	import org.agony2d.notify.AEvent;
	import org.agony2d.notify.Notifier;
	
	use namespace agony_internal;
	
	/** [ AAManager ]
	 *  [★]
	 * 		a.  vs Starling (benchmark) :
	 * 
	 * 			1.  √ render ( performance has reached 60%+ higher than starling... ) ..▲▲▲▲
	 *  		2.  √ texture ( updatable texture × painter !! ) ..▲▲
	 *  		3.  √ interact ( all kinds of touch / mouse event ) ..▲▲
	 *  		4.  √ animator ( low coupling !! ) ..▲▲▲▲
	 *  		5.  bones ( low coupling !! ) ..▲▲▲
	 *  		6.  effect ( mulitple effects can be used together... ) ..▲▲▲
	 *  		7.  √ batch (facile，can be reset at any time) ..▲
	 *  		8.  adaptation (??) ..▲
	 *  [★]
	 *  	b.  structure : → ← ↓ ↑
	 * 
	 *                    @WorldAA( A ) ← @NodeAA( A )   @BonesAA
	 *                                    ↓        ↓      ↑
	 *            @PainterAA ← @PixelsAA( A )      @FusionAA → @TextAA
	 *                         ↓          ↓        ↓     
	 *               @ImageAA( A )        ↓        @GroupAA( A ) → @StateGroupAA
	 *               ↓         ↓          ↓        ↓ 
	 * @UntingedImageAA   @TingedImageAA  ↓        @AloneGroupAA       
	 *  		                          ↓        ↓
	 *                       @AnimatorAA( A )      @BatchGroupAA
	 *                       ↓            ↓
	 *      @UntingedAnimatorAA          @TingedAnimatorAA
	 * 
	 *  ~~~ { @EffectAA ( BlurEffect × GlowEffect × DropShadowEffect... ) }
	 */
final public class AAManager extends Notifier implements IFrameUpdater{
	
	public function AAManager() {
		super(null)
	}
	
	agony_internal static function set enableErrorChecking( b:Boolean ) : void {
		if (g_enableErrorChecking != b) {
			g_enableErrorChecking = b
			if (g_context3d) {
				g_context3d.enableErrorChecking = b
			}
		}
	}
	
	agony_internal function startup( bgColor:uint,
									 hasMaskForAspectRatio:Boolean,
									 antiAlias:int, 
									 profile:String, 
									 stage3dIndex:int ) : void {
		if (!UpdaterManager.g_stage) {
			Logger.reportError("AA", "startup", "AgonyCore has not started up...!!");
		}
		if (g_orthoMatrix3d) {
			Logger.reportError("AA", "startup", "AA has been started up...!!")
		}
		g_bgColor = bgColor
		g_antiAlias = antiAlias
		g_fullWidth = NodeAA.g_fullWidth = Agony.g_fullWidth
		g_fullHeight = NodeAA.g_fullHeight = Agony.g_fullHeight
		
		NodeAA.cachedMatrix = TextureProxy.cachedMatrix = new Matrix
		NodeAA.cachedRect = TextureProxy.cachedRect = new Rectangle
		NodeAA.cachedPoint = new Point
		NodeAA.g_orthoMatrix3d = ProgramProxy.g_orthoMatrix3d = g_orthoMatrix3d = new Matrix3D
		transformOrthoMatrix(0, 0 , g_fullWidth, g_fullHeight)
		
		TouchManager.getInstance().addEventListener(ATouchEvent.NEW_TOUCH, ____onNewTouch)
		UpdaterManager.addFrameUpdater(this, UpdaterManager.GPU)
		
		g_stage3d = g_stage.stage3Ds[stage3dIndex]
		g_stage3d.addEventListener(Event.CONTEXT3D_CREATE, ____onContextCreated)
		g_stage3d.addEventListener(ErrorEvent.ERROR,       ____onError)
		g_stage3d.requestContext3D("auto", profile)
			
		trace("\n================================== [ Agony2d - Accelerated App For Mobile ] ==================================")
		Logger.reportMessage("AA", "★[ startup ]..." +
							"bgColor [ " + bgColor + " ]..." +
							"黑边遮罩 [ " + hasMaskForAspectRatio + " ]..." +
							"antiAlias [ " + antiAlias + " ]...", 2)
	}
	
	agony_internal function createWorld( width:int, height:int, invalidWhenLeave:Boolean ) : WorldAA {
		var world:WorldAA
		
		g_worldList[g_worldLength++] = world = new WorldAA(width, height, invalidWhenLeave)
		return world
	}
	
	agony_internal function getWorldAt( index:int ) : WorldAA {
		return g_worldList[index]
	}
	
	agony_internal static function removeWorld( world:WorldAA ) : void {
		var index:int
		
		index = g_worldList.indexOf(world)
		g_worldList.splice(index, 1)
		--g_worldLength
	}
	
    agony_internal static function transformOrthoMatrix( x:Number, y:Number, width:Number, height:Number ) : void {
		g_rawData[0]   =   2 / width
		g_rawData[5]   =  -2 / height
		g_rawData[12]  =  -2 * x / width  - 1
		g_rawData[13]  =   2 * y / height + 1
		g_orthoMatrix3d.copyRawDataFrom(g_rawData)
    }
	
	
	agony_internal static const PRIORITY:int = 8000 // touch
	
	agony_internal static var g_stage:Stage
	agony_internal static var g_stage3d:Stage3D
	agony_internal static var g_context3d:Context3D
	agony_internal static var g_fullWidth:int
	agony_internal static var g_fullHeight:int
	
	agony_internal static var g_bgColor:uint
	agony_internal static var g_enableErrorChecking:Boolean
	agony_internal static var g_paused:Boolean
	agony_internal static var g_antiAlias:int
	agony_internal static var g_worldList:Array = []
	agony_internal static var g_worldLength:int
	agony_internal static var g_orthoMatrix3d:Matrix3D
	agony_internal static var g_rawData:Vector.<Number> = new <Number>[ 1, 0, 0, 0, 
																		0, 1, 0, 0,
																		0, 0, 1, 0,
																		0, 0, 0, 1]
	
	
	private function ____onContextCreated( e:Event ) : void {
		var isContextExisted:Boolean
		
		isContextExisted = Boolean(g_context3d)
		g_context3d = ProgramProxy.g_context3d = g_stage3d.context3D
		
		// enalbe error checking...
		g_context3d.enableErrorChecking = g_enableErrorChecking
		
		// culling...
		g_context3d.setCulling(Context3DTriangleFace.NONE);
		
		// depth test...
		g_context3d.setDepthTest(false, Context3DCompareMode.ALWAYS);
		
		g_context3d.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA)
		
		// configure back buffer...
		g_context3d.configureBackBuffer(g_fullWidth, g_fullHeight, g_antiAlias, false)
		
		if(isContextExisted){
			Logger.reportMessage("AA", "context has been reset.")
			BuffersManager.resetAllBuffers()
			this.dispatchDirectEvent(AEvent.RESET)
		}
		else{
			Logger.reportMessage("AA", "context has been created successfully.")
			this.dispatchDirectEvent(AEvent.BEGINNING)
		}
		
		if(!g_txt){
			g_txt = new TextField
			g_txt.y = 5
			g_txt.x = 190
			g_txt.text = "0"
			g_txt.autoSize = TextFieldAutoSize.LEFT
			g_stage.addChild(g_txt)
		}
	}
	
	
	private function ____onError( e:ErrorEvent ) : void {
		Logger.reportError("AA", "____onError", "errorID:" + e.errorID + "...text:" + e.text)
	}
	
	final public function updateFrameByFrame() : void {
		var i:int
		
		if (g_paused || !g_context3d) {
			return
		}
		if(g_context3d.driverInfo == "Disposed"){
			Logger.reportWarning(this, "updateFrameByFrame", "context3d is miss...")
			g_context3d = null
			return
		}
		
		if (g_timeB >= 0) {
			g_timeB += getTimer() - g_t
		}
		g_t = getTimer()
		
		// clear...
		g_context3d.clear(((g_bgColor >> 16) & 0xff) / 255.0, 
						  ((g_bgColor >>  8) & 0xff) / 255.0, 
						   (g_bgColor        & 0xff) / 255.0)
		
		while (i < g_worldLength) {
			g_worldList[i++].render(false)
		}
		
		
		g_time += getTimer() - g_t
		if (++g_count >= 30) {
			g_txt.text = String(BuffersManager.g_buffersIndex) + "/" + String(BuffersManager.g_buffersLength) + "   | " + (g_time / g_count).toFixed(2) + " | " + (g_timeA / g_count).toFixed(2) + " | " + (g_timeB / g_count).toFixed(2)
			g_time = g_timeA = g_timeB = g_count = 0
		}
		g_t = getTimer()
		
		BuffersManager.g_buffersIndex = 0
		// render...
		g_context3d.present()
		
		g_timeA += getTimer() - g_t
		g_t = getTimer()
	}
	
	
	// debug...
	
	agony_internal static var g_txt:TextField
	agony_internal static var g_time:int
	agony_internal static var g_timeA:int
	agony_internal static var g_timeB:int = -1
	agony_internal static var g_count:int
	agony_internal static var g_t:int
	
	
	
	private static function ____onNewTouch( e:ATouchEvent ) :  void {
		var touch:Touch
		
		touch = e.touch
		____onUpdateInteract(e)
		touch.addEventListener(AEvent.MOVE,    ____onUpdateInteract, PRIORITY)
		touch.addEventListener(AEvent.RELEASE, ____onUpdateInteract, PRIORITY)
	}
	
	private static function ____onUpdateInteract( e:AEvent ) : void {
		var l:int
		var touch:Touch
		
		touch = (e is ATouchEvent) ? (e as ATouchEvent).touch : (e.target as Touch)
		l = g_worldLength
		while (--l > -1) {
			g_worldList[l].handleInteract(touch, e.m_type)
		}
	}
}
}